package org.database;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import java.util.Map.Entry;

import org.database.model.MiniDataModel;
import org.jdom.Document;
import org.jdom.Element;
import org.jdom.JDOMException;
import org.jdom.input.SAXBuilder;
import org.jdom.output.XMLOutputter;

import com.google.gson.Gson;

/**
 * 迷你数据库
 * @author Administrator
 *
 */
public class MiniDatabase {

	private static final String _uuid="_uuid";
	
	public MiniDatabase(){
	}
	
	/**
	 * 实例化时指定默认操作的数据库名称
	 * @param dbName 数据库名称
	 */
	public MiniDatabase(String dbName) {
		this.dbName = dbName;
	}
	
	/**
	 * 默认数据库名称
	 */
	private String dbName;
	public String getDbName() {
		return dbName;
	}

	public void setDbName(String dbName) {
		this.dbName = dbName;
	}
	
	
	/**
	 * 查询全表数据，使用本方法时，需在实例化时指定数据库名称，或者调用本类的setDbName(String dbName) 进行指定
	 * @param tableName 表名
	 */
	public void selectTableData(String tableName){
		seleteTableData(tableName,getDbName());
	}
	
	/**
	 * 查询全表数据 需指定表名和数据库名 
	 * @param tableName 表名
	 * @param dbName 数据库名
	 */
	public void seleteTableData(String tableName,String dbName){
		handleData(null,tableName,dbName,HandleDataType.SelectTable);
	}
	
	/**
	 * 查询单条数据，使用本方法时，需在实例化时指定数据库名称，或者调用本类的setDbName(String dbName) 进行指定
	 * @param obj 需要查询的对象，里面包含主键
	 * @param tableName 表名
	 */
	public void selectData(MiniDataModel obj,String tableName){
		selectData(obj,tableName,getDbName());
	}
	
	/**
	 * 查询单条数据，MiniDataModel 对象中_uuid有值即可。（根据主键查询）
	 * @param obj
	 * @param tableName
	 * @param dbName
	 */
	public void selectData(MiniDataModel obj,String tableName,String dbName){
		handleData(obj,tableName,dbName,HandleDataType.Select);
	}
	
	/**
	 * 从表中删除数据，使用本方法时，需在实例化时指定数据库名称，或者调用本类的setDbName(String dbName) 进行指定
	 * @param obj 需要存储的对象
	 * @param tableName 表名
	 */
	public void deleteData(MiniDataModel obj,String tableName){
		deleteData(obj,tableName,getDbName());
	}
	
	/**
	 * 删除表数据 需指定删除对象，表名和数据库名
	 * @param obj
	 * @param tableName
	 * @param dbName
	 */
	public void deleteData(MiniDataModel obj,String tableName,String dbName){
		handleData(obj,tableName,dbName,HandleDataType.Delete);
	}

	
	/**
	 * 向数据表中更新数据，使用本方法时，需在实例化时指定数据库名称，或者调用本类的setDbName(String dbName) 进行指定
	 * @param obj 需要存储的对象
	 * @param tableName 表名
	 */
	public void updateData(MiniDataModel obj,String tableName){
		updateData(obj, tableName, getDbName());
	}
	
	/**
	 * 更新数据，需指定插入对象，表名和数据库名
	 * @param obj 需要插入的对象
	 * @param tableName 表名
	 * @param dbName 数据库名
	 */
	public void updateData(MiniDataModel obj,String tableName,String dbName){
		handleData(obj,tableName,dbName,HandleDataType.Update);
	}
	
	

	/**
	 * 向数据表中插入数据，使用本方法时，需在实例化时指定数据库名称，或者调用本类的setDbName(String dbName) 进行指定
	 * @param obj 需要存储的对象
	 * @param tableName 表名
	 */
	public void insertData(MiniDataModel obj,String tableName){
		insertData(obj, tableName , getDbName());
	}
	
	/**
	 * 插入数据，需指定插入对象，表名和数据库名
	 * @param obj 对象
	 * @param tableName 表名
	 * @param dbName 数据库名
	 */
	public void insertData(MiniDataModel obj,String tableName,String dbName){
		if(isNull(tableName,"请输入表名")){
			return;
		}
		if(isNull(dbName,"请指定数据库")){
			return;
		}
		File file = new File(dbName+".xml");
		if(!file.exists()){
			System.out.println("数据库："+dbName+"不存在");
			return;
		}
		
		try {
			FileInputStream inputStream = new FileInputStream(file);
			SAXBuilder saxBuilder = new SAXBuilder();
			Document document = saxBuilder.build(inputStream);
			//获取数据库
			Element root = document.getRootElement();
			
			//获取数据表
			Element table = root.getChild(tableName);
			if(table==null){
				System.out.println("数据表："+tableName+"不存在。");
				return ; 
			}
				//插入主键
				String primaryKey = UUID.randomUUID().toString();
				Gson gson = new Gson();
				
				//将对象转换为json格式，再转换为map对象
				String dataJson = gson.toJson(obj);
				
				if(obj.get_uuid()!=null){
					primaryKey = obj.get_uuid();
				}
				
				//new一个data，并将map转换为json格式插入。
				Element data = new Element(primaryKey);
				data.addContent(dataJson);
				table.addContent(data);
				System.out.println("新增数据成功！");
				outputXml(document, file);
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
		
	}

	
	/**
	 * 删除表操作，使用本方法时，需在实例化时指定数据库名称，或者调用本类的setDbName(String dbName) 进行指定
	 * @param tableName
	 */
	public void deleteTable(String tableName){
		deleteTable(tableName,getDbName());
	}
	
	/**
	 * 删除表操作
	 * @param tableName 表名
	 * @param dbName 数据库名
	 */
	public void deleteTable(String tableName,String dbName){
		handleTable(tableName,dbName,HandleTableType.Delete);
	}
	
	
	/**
	 * 添加表，使用本方法时，需在实例化时指定数据库名称，或者调用本类的setDbName(String dbName) 进行指定
	 * @param tableName 数据表名称
	 */
	public void insertTable(String tableName){
		insertTable(tableName,getDbName());
	}
	
	
	/**
	 * 添加表操作
	 * @param tableName 表名
	 * @param dbName 数据库名
	 */
	public void insertTable(String tableName,String dbName){
		handleTable(tableName,dbName,HandleTableType.Insert);
	}
	
	public void deleteDatabase(String dbName){
		if(dbName==null || "".equals(dbName)){
			System.out.println("请输入数据库名称");
			return ;
		}
		File file = new File(dbName+".xml");
		if(file.exists()){
			System.out.println("数据库："+dbName+" 已删除。");
		}else{
			System.out.println("不存在此数据库。");
		}
	}
	
	/**
	 * 根据名称创建数据库
	 * @param dbName 数据库名称
	 */
	public void createDatabase(String dbName){
		if(dbName==null || "".equals(dbName)){
			System.out.println("请输入数据库名称");
			return ;
		}
		File file = new File(dbName+".xml");
		if(file.exists()){
			System.out.println("数据库:"+dbName+" 已经存在，请更换其他名称");
			return ; 
		}
		Document document = new Document();
		Element root = new Element("database");
		document.setRootElement(root);
		//文件输出
		outputXml(document, file);
		System.out.println("数据库："+dbName+"创建成功！");
	}

	
	/**
	 * 在指定的数据库中添加表或者删除表
	 * @param tableName 数据库名称
	 * @param dbName 表名称
	 * @param handleType 枚举类型，指定是新增还是删除表
	 */
	private void handleTable(String tableName,String dbName,HandleTableType handleType ){
		if(isNull(tableName,"请输入表名")){
			return;
		}
		if(isNull(dbName,"请指定数据库")){
			return;
		}
		File file = new File(dbName+".xml");
		if(!file.exists()){
			System.out.println("数据库："+dbName+"不存在");
			return;
		}
			try {
				FileInputStream inputStream = new FileInputStream(file);
				SAXBuilder saxBuilder = new SAXBuilder();
				Document document = saxBuilder.build(inputStream);
				Element root = document.getRootElement();
				//如果为true则添加，否则删除
				if(handleType == HandleTableType.Insert){
					//执行添加表操作
					if(root.getChild(tableName)==null){
						Element table = new Element(tableName);
						root.addContent(table);
						//文件输出
						outputXml(document,file);
						System.out.println("数据表："+tableName+"创建成功！");
					}else{
						System.out.println("数据表："+tableName+"已经存在");
					}
				}else if(handleType == HandleTableType.Delete){
					//执行删除表操作
					if(root.getChild(tableName)!=null){
						root.removeChild(tableName);
						outputXml(document,file);
						System.out.println("数据表："+tableName+"删除成功！");
					}else{
						System.out.println("数据表："+tableName+"不存在，无法删除。");
					}
				}
				
			} catch (FileNotFoundException e) {
				e.printStackTrace();
			} catch (JDOMException e) {
				e.printStackTrace();
			} catch (IOException e) {
				e.printStackTrace();
			}
				
	}
	
	
	
	/**
	 * 处理数据，对数据更新或者删除
	 * @param obj 对象
	 * @param tableName 表名
	 * @param dbName 数据库名
	 * @param handleType 指定是更新，删除还是单条查询
	 */
	private void handleData(MiniDataModel obj,String tableName,String dbName,HandleDataType handleType){
		if(isNull(tableName,"请输入表名")){
			return;
		}
		if(isNull(dbName,"请指定数据库")){
			return;
		}
		File file = new File(dbName+".xml");
		if(!file.exists()){
			System.out.println("数据库："+dbName+"不存在");
			return;
		}
		
		try {
			FileInputStream inputStream = new FileInputStream(file);
			SAXBuilder saxBuilder = new SAXBuilder();
			Document document = saxBuilder.build(inputStream);
			//获取数据库
			Element root = document.getRootElement();
			
			//获取数据表
			Element table = root.getChild(tableName);
			
			if(table==null){
				System.out.println("数据表："+tableName+"不存在。");
				return ; 
			}
			
			
			Gson gson = new Gson();
			
			//如果是扫描全表则直接输出。
			if(handleType==HandleDataType.SelectTable){
				//获取所有数据并输出。
				List<Element> dataList = table.getChildren();
				for (Element element : dataList) {
					String pKey = element.getName();
					String dataValue = element.getText();
					Map<String,Object> map = gson.fromJson(dataValue, Map.class);
					System.out.print(_uuid+":"+pKey+"\t");
					for (Entry<String,Object> entry : map.entrySet()) {
						if(!entry.getKey().equals(_uuid))
						System.out.print(entry.getKey()+":"+entry.getValue()+"\t");
					}
					System.out.println("");
				}
				return;
			}
			
			
			//将对象转换为json格式，再转换成map对象
			
			String dataJson = gson.toJson(obj);
			//判断主键是否为空
			if(obj.get_uuid()==null){
				System.out.println("对象缺少主键：_uuid");
				return;
			}
			String uuid = obj.get_uuid();
			
			//获取表中是否有此数据，如果有则更新数据。
			Element data = table.getChild(uuid);
			if(data==null){
				System.out.println("对应的主键不正确，没有对应该记录"+uuid);
				return;
			}
			
			//1.第一个处理为更新
			//2.第二个处理为删除
			//3.第三个处理为单条记录查询
			if(handleType==HandleDataType.Update){
				table.removeChild(uuid);
				data = new Element(uuid);
				data.addContent(dataJson);
				table.addContent(data);
				//输出文件
				outputXml(document, file);
			}else if(handleType==HandleDataType.Delete){
				table.removeChild(uuid);
				//输出文件
				outputXml(document, file);
			}else if(handleType==HandleDataType.Select){
				String dataValue = data.getText();
				Map<String,Object> map = gson.fromJson(dataValue, Map.class);
				System.out.println(_uuid+":"+uuid);
				for (Entry<String,Object> entry : map.entrySet()) {
					if(entry.getKey()!=_uuid)
					System.out.println(entry.getKey()+":\t"+entry.getValue());
				}
			}
			
		} catch (FileNotFoundException e) {
			e.printStackTrace();
		} catch (JDOMException e) {
			e.printStackTrace();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}
	
	
	/**
	 * 非空验证
	 * @param str
	 * @param message 验证为空时的消息提示
	 * @return
	 */
	private boolean isNull(String str,String message){
		if(str==null||"".equals(str)){
			System.out.println(message);
			return true;
		}
		return false;
	}
	
	
	/**
	 * 根据Document，和文件进行输出
	 * @param document
	 * @param outputStream
	 */
	private void outputXml(Document document,File file){
		XMLOutputter out = new XMLOutputter();
		try {
			FileOutputStream outputStream = new FileOutputStream(file);
			out.output(document, outputStream);
			outputStream.close();
		} catch (IOException e) {
			e.printStackTrace();
		}
	}

	/**
	 * 执行数据操作，修改，删除，单条查询和全表查询
	 * @author Administrator
	 *
	 */
	private enum HandleDataType{
		Update(1),Delete(2),Select(3), SelectTable(4);
		private int handle;
		private HandleDataType(int handle){
			this.handle = handle;
		}
		
	}
	
	/**
	 * 执行表操作，新增和删除
	 * @author Administrator
	 *
	 */
	private enum HandleTableType{
		Insert(1),Delete(2);
		private int handle;
		private HandleTableType(int handle){
			this.handle=handle;
		}
		
	}
	
}
